﻿using System.Collections.Generic;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using Sinacor.Infra.Service.Validation.BusinessRules;

namespace Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessLogic.BusinessRulesValidation
{
    /// <summary>
    /// BusinessLogic.BusinessRulesValidation Class - ValidadorInclusaoDetalheOrdem
    /// </summary>
    public class ValidadorInclusaoDetalheOrdem : BaseBusinessRuleValidation<BusinessEntity.DetalheOrdem>, IBusinessRuleValidation
    {
        #region Constructores

        /// <summary>
        /// Construtor ValidadorInclusaoDetalheOrdem
        /// </summary>
        /// <param name="ordemMae">BusinessEntity.Ordem</param>
        public ValidadorInclusaoDetalheOrdem(BusinessEntity.Ordem ordemMae) 
        {
            this._ordemMae = ordemMae;
        }

        #endregion

        #region Atributos

        private BusinessEntity.Ordem _ordemMae;

        #endregion

        #region Propriedades

        /// <summary>
        /// Ordem mãe
        /// </summary>
        public BusinessEntity.Ordem OrdemMae
        {
            get { return _ordemMae; }
            set { _ordemMae = value; }
        }

        #endregion

        #region Métodos Públicos

        /// <summary>
        /// GetErrors
        /// </summary>
        public List<Infra.Common.Validation.ValidationError> GetErrors()
        {
            return base.ErrorsList;
        }

        /// <summary>
        /// Validate
        /// </summary>
        public void Validate()
        {
            if (this.ValidarMercado())
            {
                //Mercado do detalhe da ordem é Termo?
                if (EntityToValidate.Mercado == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Termo)
                {
                    //Prazo do vencimento do termo é maior que zero?
                    if (EntityToValidate.PrazoVencimentoTermo.HasValue && EntityToValidate.PrazoVencimentoTermo > 0)
                        this.ExecutarRotinaIsolada1();
                    else
                        base.AddValidationError(109429);
                }
                else
                {
                    //Assumir prazo do vencimento do termo como nulo
                    EntityToValidate.PrazoVencimentoTermo = null;

                    this.ExecutarRotinaIsolada1();
                }
            }
        }

        #endregion

        #region Métodos Privados

        /// <summary>
        /// ExecutarRotinaIsolada
        /// </summary>
        private void ExecutarRotinaIsolada1()
        {
            if (this.ValidarAtivo())
            {
                //Sistema parametrizado para verificação de bloqueio de cliente?
                if (new Parametro.BusinessLogic.Parametro().ObterParametro().BloqueioClientes)
                {
                    //Verificar se cliente da ordem está bloqueado 
                    if (this.VerificarClienteBloqueado())
                        this.ExecutarRotinaIsolada2();
                }
                else
                    this.ExecutarRotinaIsolada2();
            }
        }

        /// <summary>
        /// ExecutarRotinaIsolada2
        /// </summary>
        private void ExecutarRotinaIsolada2()
        {
            //Preço da ordem é maior que zero?
            if (EntityToValidate.Preco.HasValue && EntityToValidate.Preco > 0)
            {
                if (this.ValidarCarteira())
                    this.ValidarQuantidade();
            }
            else
                base.AddValidationError(109430);
        }

        /// <summary>
        /// ConverterToTituloTipoMercado
        /// </summary>
        /// <param name="tipoMercado">Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado</param>
        /// <returns>Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado</returns>
        private Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado ConverterToTituloTipoMercado(Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado tipoMercado)
        {
            switch (tipoMercado)
            {
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Vista:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Vista;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoCompra:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.OpcaoCompra;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoVenda:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.OpcaoVenda;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoCompra:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoCompra;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoVenda:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoVenda;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Fracionario:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Fracionario;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Estruturado:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Estruturado;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Leilao:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Leilao;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Termo:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Termo;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.LeilaoNaoCotados:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.LeilaoNaoCotados;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Futuro:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.Futuro;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.VencimentoFuturo:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.VencimentoFuturo;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.BOX:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.BOX;                    
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.CotasETF:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.CotasETF;                    
                default:
                    return Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Enum.TipoMercado.IntegralizacaoResgateCotas;
                
            }
        }

        /// <summary>
        /// ConverterToCarteiraTipoMercado
        /// </summary>
        /// <param name="tipoMercado">Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado</param>
        /// <returns>Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado</returns>
        private Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado ConverterToCarteiraTipoMercado(Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado tipoMercado)
        {
            switch (tipoMercado)
            {
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Vista:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Vista;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoCompra:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.OpcaoCompra;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoVenda:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.OpcaoVenda;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoCompra:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoCompra;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoVenda:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.ExercicioOpcaoVenda;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Fracionario:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Fracionario;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Estruturado:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Estruturado;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Leilao:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Leilao;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Termo:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Termo;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.LeilaoNaoCotados:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.LeilaoNaoCotados;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Futuro:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.Futuro;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.VencimentoFuturo:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.VencimentoFuturo;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.BOX:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.BOX;
                case Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.CotasETF:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.CotasETF;
                default:
                    return Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoMercado.IntegralizacaoResgateCotas;

            }
        }

        /// <summary>
        /// TratarExcecoes
        /// </summary>
        /// <param name="request">BusinessEntity.Resultado</param>
        private void TratarExcecoes(BusinessEntity.Resultado request)
        {
            if (string.IsNullOrEmpty(request.Argumentos))
                throw new BusinessException(request.CodigoErro);
            else
                ErrorMessage.ConvertXmlArgumentsToParameters(request.Argumentos);
        }

        /// <summary>
        /// ValidarAtivo
        /// </summary>
        /// <returns>Boolean</returns>
        private bool ValidarAtivo()
        {
            //Ativo foi informado?
            if (string.IsNullOrEmpty(EntityToValidate.Ativo))
                base.AddValidationError(109258);
            else
            {
                //Obter informações do ativo
                Titulo.BusinessEntity.Titulo param = new Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Titulo();

                param.DataOrdem = EntityToValidate.DataOrdem;
                param.Codigo = EntityToValidate.Ativo;
                param.TipoMercado = this.ConverterToTituloTipoMercado(EntityToValidate.Mercado.Value);

                Titulo.BusinessEntity.Titulo titulo = new Titulo.BusinessLogic.Titulo().ObterTitulo(param);

                //Ativo informado existe?
                if (titulo == null || string.IsNullOrEmpty(titulo.Codigo))
                    base.AddValidationError(109252);
                else
                {
                    //Atribuir dados do ativo do detalhe para a ordem mãe.
                    //Isso é feito pois a ordem mãe irá gerar a ordem que será incluída na base de dados (detalhe no final é uma ordem).
                    this._ordemMae.CodigoISIN = titulo.CodigoISIN;

                    //Gerar código do casamento para a ordem
                    BusinessEntity.Resultado result = new ResourceAccess.ManutencaoOrdens().GerarCodigoCasamento
                        (
                            new BusinessEntity.Ordem
                            {
                                Ativo = EntityToValidate.Ativo,
                                CodigoISIN = titulo.CodigoISIN,
                                Mercado = EntityToValidate.Mercado,
                                PrazoVencimentoTermo = EntityToValidate.PrazoVencimentoTermo
                            }
                        );

                    //Houve erro na geração do código do casamento?
                    if (!result.Sucesso)
                    {
                        //Código do erro gerado é igual a -1?
                        if (result.CodigoErro == -1)
                            base.AddValidationError(109281);
                        else
                            //Tratar exceção gerada
                            this.TratarExcecoes(result);
                    }
                    else
                    {
                        //Armazenar na ordem o código do casamento
                        this._ordemMae.CodigoCasamento = result.Valor.ToString();
                    }
                }
            }

            return base.ErrorsList.Count == 0;
        }

        /// <summary>
        /// ValidarCarteira
        /// </summary>
        /// <returns>Boolean</returns>
        private bool ValidarCarteira()
        {
            //Carteira foi informada?
            if (EntityToValidate.Carteira.HasValue)
            {
                //Validar se carteira informada existe
                Carteira.BusinessEntity.Parameter.ValidarCarteiraMercado param = new Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Parameter.ValidarCarteiraMercado();

                param.CodigoBolsa = _ordemMae.CodigoBolsa;
                param.CodigoCarteira = (int)EntityToValidate.Carteira;
                param.CodigoMercado = this.ConverterToCarteiraTipoMercado(EntityToValidate.Mercado.Value);
                
                if (EntityToValidate.NaturezaOperacao == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.NaturezaOperacao.Compra)
                    param.NaturezaOperacao = Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoNaturezaOperacao.Compra;
                else
                    param.NaturezaOperacao = Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoNaturezaOperacao.Venda;

                if (!new Carteira.BusinessLogic.Carteira().ValidarCarteiraMercado(param))
                    base.AddValidationError(109271);
            }
            else
            {
                //Assumir a carteira de menor prioridade que atenda a Bolsa/Mercado/Natureza da Ordem
                Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Parameter.ConsultarMenorCarteira param =
                            new Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Parameter.ConsultarMenorCarteira();

                param.CodigoBolsa = _ordemMae.CodigoBolsa;
                param.CodigoMercado = this.ConverterToCarteiraTipoMercado(EntityToValidate.Mercado.Value);                

                if (EntityToValidate.NaturezaOperacao == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.NaturezaOperacao.Compra)
                    param.NaturezaOperacao = Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoNaturezaOperacao.Compra;
                else
                    param.NaturezaOperacao = Sinacor.Servico.Bovespa.Carteira.BusinessEntity.Enum.TipoNaturezaOperacao.Venda;

                EntityToValidate.Carteira = (short?)new Carteira.BusinessLogic.Carteira().ConsultarMenorCarteira(param).CodigoCarteira;
            }               
            
            return base.ErrorsList.Count == 0;
        }

        /// <summary>
        /// ValidarMercado
        /// </summary>
        /// <returns>Boolean</returns>
        private bool ValidarMercado()
        {
            //Ativo é POP
            if (!string.IsNullOrEmpty(_ordemMae.Ativo) && _ordemMae.CodigoISIN.Substring(6, 2).Equals("PP"))
            {
                if (EntityToValidate.Mercado != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Vista &&
                    EntityToValidate.Mercado != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoCompra &&
                    EntityToValidate.Mercado != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.OpcaoVenda)
                    base.AddValidationError(109428);
            }

            //Tratamento para ordem do tipo financiamento
            if (_ordemMae.TipoOrdem == Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoOrdem.Financiamento)
            {
                if(EntityToValidate.Mercado != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Vista &&
                    EntityToValidate.Mercado != Sinacor.Servico.Bovespa.ManutencaoOrdens.BusinessEntity.Enum.TipoMercado.Termo)
                    base.AddValidationError(109428);
            }

            return base.ErrorsList.Count == 0;
        }

        /// <summary>
        /// ValidarQuantidade
        /// </summary>
        /// <returns>Boolean</returns>
        private bool ValidarQuantidade()
        {
            if (EntityToValidate.Quantidade <= 0)                
                base.AddValidationError(109272);                
            else
            {
                //Consultar lote mínimo do ativo
                Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Parameter.ConsultarLoteMinimoNegociacao param = new Sinacor.Servico.Bovespa.Titulo.BusinessEntity.Parameter.ConsultarLoteMinimoNegociacao();

                param._Titulo = EntityToValidate.Ativo;
                param._DataPregao = new Movimento.BusinessLogic.ControleMovimentoOrdens().ObterInformacoesMovimento(new Movimento.BusinessEntity.DataMovimento()).DataMovimento;

                int? loteMinimo = new Titulo.BusinessLogic.Titulo().ConsultarLoteMinimoNegociacao(param);
                
                if (loteMinimo == 0) 
                    loteMinimo = 1;

                //Quantidade da ordem é múltiplo do lote mínimo?                
                if ((EntityToValidate.Quantidade % loteMinimo) != 0)                
                    base.AddValidationError(109312);                
            }           

            return base.ErrorsList.Count == 0;
        }

        /// <summary>
        /// VerificarClienteBloqueado
        /// </summary>
        /// <returns>Boolean</returns>
        private bool VerificarClienteBloqueado()
        {
            BusinessEntity.Resultado resultado = null;

            BusinessEntity.Ordem ordem = new BusinessEntity.Ordem();
            ordem.CodigoCliente = _ordemMae.CodigoCliente;
            ordem.Mercado = EntityToValidate.Mercado;
            ordem.NaturezaOperacao = EntityToValidate.NaturezaOperacao;
            ordem.Ativo = EntityToValidate.Ativo;

            //Data de validade da ordem mãe é menor que a data do sistema?
            if (_ordemMae.DataValidade < new Movimento.BusinessLogic.ControleMovimentoOrdens().ObterInformacoesMovimento(new Movimento.BusinessEntity.DataMovimento()).DataMovimento)
            {
                //Tipo validação por DataValidade
                ordem.DataValidade = _ordemMae.DataValidade;
                resultado = new ResourceAccess.ManutencaoOrdens().VerificarClienteBloqueado(ordem, BusinessEntity.Enum.TipoValidacaoClienteBloqueado.PorDataValidade);
            }
            else
            {
                //Tipo validação por DataPregao
                ordem.DataOrdem = EntityToValidate.DataOrdem;
                resultado = new ResourceAccess.ManutencaoOrdens().VerificarClienteBloqueado(ordem, BusinessEntity.Enum.TipoValidacaoClienteBloqueado.PorDataPregao);
            }

            //Existe bloqueio para o cliente?
            //Objeto de retorno da ação anterior (BusinessEntity.Resultado) possui a propriedade Sucesso com o valor falso.
            if (!resultado.Sucesso)
            {
                if (resultado.CodigoErro == -1)                
                    base.AddValidationError(109414);                
                else
                    this.TratarExcecoes(resultado);                
            }

            return base.ErrorsList.Count == 0;
        }

        #endregion
    }
}